/* Module.vala
 *
 * Copyright (C) 2008  Raphael Bosshard
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 *
 * Author:
 * 	Raphael Bosshard <raphael.bosshard@gmail.com>
 *
 * Many thanks to the developers of Rhythmbox, Gedit and Epiphany. This code 
 * here is just a shameless port of the plugin-interfaces found in those gems.
 * 
 */

using GLib;

errordomain Appendage.ModuleError{
	MODULE_NOT_ENABLED
	
}

errordomain Appendage.ModuleLoaderError {
	LOADER_NOT_FOUND,
	LOADING_ERROR,
	SYMBOL_ERROR
	
}

public abstract class Appendage.ModuleLoader <T>: Object {

	public abstract void load () throws Appendage.ModuleLoaderError ;
	
	public abstract T new_object ();
}



public class Appendage.ModuleLoaderC <T> : Appendage.ModuleLoader <T> {

	private GLib.Module library;
	private GLib.Type mtype;

	private delegate Type ModuleRegisterFunction ();

	public string register_function_name { get; construct; }

	public string path { get; construct; }

	public ModuleLoaderC (string path) {
		this.path = path;
		assert (GLib.Module.supported ());
		this.register_function_name = Appendage.REGISTER_FUNCTION_NAME;
		
	}
	


	public override void load () throws Appendage.ModuleLoaderError  {
		void* function;
		GLib.debug ("Loading module at: '%s'", this.path);
	    
		this.library = Module.open (this.path, ModuleFlags.BIND_LAZY);	
	
		if (this.library == null) {
			
			GLib.warning("Could not load module file: %s", GLib.Module.error());
			throw new Appendage.ModuleLoaderError.LOADING_ERROR ("Could not load module file: %s", GLib.Module.error());
		}
	
		/* extract symbols from the lib */
		if (!library.symbol (this.register_function_name, out function)) {
			GLib.warning ("Could not extract symbols: %s", GLib.Module.error());
			throw new Appendage.ModuleLoaderError.SYMBOL_ERROR ("Could not extract symbols: %s", GLib.Module.error());
		}
	
		/* symbol can still be null even though GLib.Module.symbol ()
		 * returned true */	
		GLib.assert (function != null);
	
		ModuleRegisterFunction register_library = (ModuleRegisterFunction)function;
				
		this.mtype = register_library ();
		
		GLib.message ("Module is of type '%s'", this.mtype.name ());
	
		if (this.mtype == 0) {
			GLib.warning("Incomplete library contained by module '%s'", this.path);
			throw new Appendage.ModuleLoaderError.SYMBOL_ERROR ("Incomplete library in module '%s'", this.path);
		}
		    
		return;
	}
    
	public override T new_object () {
	  
		return Object.new (this.mtype);
	}
        
}


 
/*

public void main (string[] args) {
	Appendage.Plugin test;

	if (args.length < 2){
		stdout.printf ("Usage: simplemoduleloaderexample <path-to-plugin.so>");
		return;
	}

	stdout.printf("Loading Module '%s'\n", args[1]);
	
//	var loader = new Appendage.ModuleLoaderC <Appendage.Plugin> ("./libplugin.so");

	Appendage.ModuleLoader <Appendage.Plugin> loader = new Appendage.ModuleLoaderC <Appendage.Plugin> ("./libplugin.so");

	GLib.message ("loader type is: '%s'", loader.get_type().name () );

	try {
		loader.load ();
	} catch (Appendage.ModuleLoaderError e) {
		GLib.error ("Could not load module!");
		return;
	}
	
	test = loader.new_object ();
	
	test.test ();
	
	return;
}
 */ 
